using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using System.Web.UI;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using System.Web.UI.WebControls;

namespace TST.SharePoint.Shared.WebControls
{
    public class SharePointGrid : SPGridView
    {
        private System.Data.DataView _view;
        private Exception _exception;
        private GridDatasource _datasource;
        private DatasourceConfiguration _configuration;
        private List<IFilterValues> _filterProviders;
        private bool _customSort;
        private string _sortExpression;

        public SharePointGrid(GridDatasource datasource, DatasourceConfiguration configuration)
        {
            _filterProviders = new List<IFilterValues>();
            _datasource = datasource;
            _configuration = configuration;
        }

        public GridDatasource GridDatasource
        {
            get { return _datasource; }
            set
            {
                _datasource = value;
                ResetConfiguration();
            }
        }

        public DatasourceConfiguration Configuration
        {
            get { return _configuration; }
            set
            {
                _configuration = value;
                if (_datasource != null)
                {
                    _datasource.Configuration = _configuration;
                }
                ResetConfiguration();
            }
        }

        private void ResetConfiguration()
        {
            SetPaging();
            SetGroupAndSort();
            SetColumns();
        }

        /// <summary>
        /// Create the SharePoint grid. Uses the SharePoint SPGridView object.
        /// </summary>
        protected override void CreateChildControls()
        {
            SetColumns();
        }

        protected override void OnInit(EventArgs args)
        {
            EnableViewState = true;
            AutoGenerateColumns = false;
            Width = Unit.Percentage(100);
            AllowSorting = true;
            // Add event handler for sorting.
            Sorting += new GridViewSortEventHandler(_spGrid_Sorting);

            if (GridDatasource != null && Configuration.Fields.Count>0)
            {
                if (Configuration != null)
                {
                    SetPaging();
                    SetGroupAndSort();
                }
            }
            base.OnInit(args);
        }

        private void SetPaging()
        {
            // Set paging options.
            if (Configuration.PageSize > 0)
            {
                PagerSettings.Position = Configuration.PagerPosition;
                if (Configuration.PageButtonCount > 0)
                    PagerSettings.PageButtonCount = Configuration.PageButtonCount;
                PagerSettings.NextPageText = TST.SharePointObjects.ContentWebParts.TST_SharePointObjects_ContentWebParts.NextButtonText;
                PagerSettings.PreviousPageText = TST.SharePointObjects.ContentWebParts.TST_SharePointObjects_ContentWebParts.PreviousButtonText;
                PagerSettings.FirstPageText = TST.SharePointObjects.ContentWebParts.TST_SharePointObjects_ContentWebParts.FirstButtonText;
                PagerSettings.LastPageText = TST.SharePointObjects.ContentWebParts.TST_SharePointObjects_ContentWebParts.LastButtonText;
                switch (Configuration.PagerMode)
                {
                    case TST.SharePoint.Shared.DatasourceConfiguration.GridPagerMode.NextPreviousButtons:
                        PagerSettings.Mode = PagerButtons.NextPrevious;
                        PagerSettings.NextPageImageUrl = string.Empty;
                        PagerSettings.LastPageImageUrl = string.Empty;
                        PagerSettings.PreviousPageImageUrl = string.Empty;
                        PagerSettings.FirstPageImageUrl = string.Empty;
                        break;
                    case TST.SharePoint.Shared.DatasourceConfiguration.GridPagerMode.NextPreviousFirstLastButtons:
                        PagerSettings.Mode = PagerButtons.NextPreviousFirstLast;
                        PagerSettings.NextPageImageUrl = string.Empty;
                        PagerSettings.LastPageImageUrl = string.Empty;
                        PagerSettings.PreviousPageImageUrl = string.Empty;
                        PagerSettings.FirstPageImageUrl = string.Empty;
                        break;
                    case TST.SharePoint.Shared.DatasourceConfiguration.GridPagerMode.NextPreviousFirstLastImages:
                        PagerSettings.Mode = PagerButtons.NextPreviousFirstLast;
                        PagerSettings.NextPageImageUrl = "/_layouts/images/ewr020.gif";
                        PagerSettings.LastPageImageUrl = "/_layouts/images/ewr021.gif";
                        PagerSettings.PreviousPageImageUrl = "/_layouts/images/ewr019.gif";
                        PagerSettings.FirstPageImageUrl = "/_layouts/images/ewr018.gif";
                        break;
                    case TST.SharePoint.Shared.DatasourceConfiguration.GridPagerMode.NextPreviousImages:
                        PagerSettings.Mode = PagerButtons.NextPrevious;
                        PagerSettings.NextPageImageUrl = "/_layouts/images/ewr020.gif";
                        PagerSettings.LastPageImageUrl = string.Empty;
                        PagerSettings.PreviousPageImageUrl = "/_layouts/images/ewr019.gif";
                        PagerSettings.FirstPageImageUrl = string.Empty;
                        break;
                    case TST.SharePoint.Shared.DatasourceConfiguration.GridPagerMode.NumericButtons:
                        PagerSettings.Mode = PagerButtons.Numeric;
                        PagerSettings.NextPageImageUrl = string.Empty;
                        PagerSettings.LastPageImageUrl = string.Empty;
                        PagerSettings.PreviousPageImageUrl = string.Empty;
                        PagerSettings.FirstPageImageUrl = string.Empty;
                        break;
                    case TST.SharePoint.Shared.DatasourceConfiguration.GridPagerMode.NumericFirstLastButtons:
                        PagerSettings.Mode = PagerButtons.NumericFirstLast;
                        PagerSettings.NextPageImageUrl = string.Empty;
                        PagerSettings.LastPageImageUrl = string.Empty;
                        PagerSettings.PreviousPageImageUrl = string.Empty;
                        PagerSettings.FirstPageImageUrl = string.Empty;
                        break;
                }
                PageSize = Configuration.PageSize;
                AllowPaging = true;
                PageIndexChanging += new GridViewPageEventHandler(SharePointGrid_PageIndexChanging);
            }
            else
            {
                AllowPaging = false;
            }
        }

        private void SetColumns()
        {
            if (GridDatasource == null)
                return;

            Columns.Clear();

            // Iterate through selected fieldreferences and add fields to the grid.
            foreach (FieldReference fieldRef in Configuration.Fields)
            {
                // Get the field from the datasource.
                if (GridDatasource.Fields.Contains(fieldRef.Id))
                {
                    Field field = GridDatasource.Fields[fieldRef.Id];

                    // Check if this field has a LinkField assigned. If so add a column that renders a link.
                    if (Field.IsFieldId(fieldRef.LinkFieldId) && GridDatasource.Fields.Contains(fieldRef.LinkFieldId))
                    {
                        // Get the linkfield. This is also a field in the fields collection.
                        Field linkField = GridDatasource.Fields[fieldRef.LinkFieldId];

                        // If the linkfield is LinkUrl, add a dropdown menu to the items in the grid.
                        // TODO: Make an option in the configuration to select the menu field.
                        if (linkField.Id == Configuration.ItemMenu)
                        {
                            // Create a new Menu field.
                            SPMenuField menuColumn = new SPMenuField();
                            if (!string.IsNullOrEmpty(fieldRef.DisplayName))
                                menuColumn.HeaderText = fieldRef.DisplayName;
                            else
                                menuColumn.HeaderText = field.DisplayName;
                            menuColumn.TextFields = field.Name;
                            menuColumn.MenuTemplateId = "ItemMenu";
                            menuColumn.NavigateUrlFields = linkField.Name;
                            menuColumn.NavigateUrlFormat = "{0}";
                            menuColumn.TokenNameAndValueFields = "webid=WebId,siteid=SiteId,listid=ListId,id=ID";
                            menuColumn.SortExpression = field.Name;

                            // Add the menu column to the grid.
                            Columns.Add(menuColumn);
                        }
                        else
                        {
                            // Add the column as a HyperLinkField.
                            HyperLinkField urlColumn = new HyperLinkField();
                            urlColumn.DataNavigateUrlFormatString = "{0}";
                            urlColumn.DataTextField = field.Name;
                            urlColumn.DataNavigateUrlFields = new string[] { linkField.Name };
                            if (!string.IsNullOrEmpty(fieldRef.DisplayName))
                                urlColumn.HeaderText = fieldRef.DisplayName;
                            else
                                urlColumn.HeaderText = field.DisplayName;
                            urlColumn.SortExpression = field.Name;
                            if (Configuration.OpenAllLinksInNewWindows || fieldRef.OpenInNewWindow)
                                urlColumn.Target = "_blank";
                            urlColumn.NavigateUrl = "";
                            Columns.Add(urlColumn);
                        }
                    }
                    else
                    {
                        // Field has no LinkField assigned. Add a column based on the fieldtype.
                        switch (field.FieldType)
                        {
                            case FieldType.Boolean:
                                AddBooleanColumn(field, fieldRef);
                                break;
                            case FieldType.Calculated:
                                switch (((FieldCalculated)field).OutputFieldType)
                                {
                                    case FieldType.Boolean:
                                        AddBooleanColumn(field, fieldRef);
                                        break;
                                    case FieldType.Currency:
                                        AddCurrencyColumn(field, ((WSSFieldCalculated)field).CurrencyLocaleId, fieldRef);
                                        break;
                                    default:
                                        AddDefaultColumn(field, fieldRef);
                                        break;
                                }
                                break;
                            case FieldType.Currency:
                                WSSFieldCurrency currencyField = field as WSSFieldCurrency;
                                if (currencyField != null)
                                    AddCurrencyColumn(field, currencyField.CurrencyLocaleId, fieldRef);
                                else
                                    AddCurrencyColumn(field, 0, fieldRef);
                                break;
                            case FieldType.Hyperlink:
                                TemplateField linkColumn = new TemplateField();
                                linkColumn.HeaderStyle.HorizontalAlign = GetHorizontalAlign(field);
                                linkColumn.ItemStyle.HorizontalAlign = GetHorizontalAlign(field);
                                WSSFieldHyperlink linkField = field as WSSFieldHyperlink;
                                if (linkField == null)
                                    linkColumn.ItemTemplate = new HyperlinkColumn(field.Name, false);
                                else
                                    linkColumn.ItemTemplate = new HyperlinkColumn(field.Name, linkField.RenderAsImage);
                                if (!string.IsNullOrEmpty(fieldRef.DisplayName))
                                    linkColumn.HeaderText = fieldRef.DisplayName;
                                else
                                    linkColumn.HeaderText = field.DisplayName;
                                Columns.Add(linkColumn);
                                break;
                            default:
                                AddDefaultColumn(field, fieldRef);
                                break;
                        }
                    }
                }
            }
        }

        private void AddCurrencyColumn(Field field, int currencyLocaleId, FieldReference fieldRef)
        {
            // Add a column that renders a currency based on the culture set on the field.
            TemplateField currencyColumn = new TemplateField();
            currencyColumn.HeaderStyle.HorizontalAlign = GetHorizontalAlign(field);
            currencyColumn.ItemStyle.HorizontalAlign = GetHorizontalAlign(field);

            // Get formatting
            string formatting = string.Empty;
            IDataFormatting format = field as IDataFormatting;
            if (formatting != null)
            {
                formatting = format.DataFormatString;
            }

            if (currencyLocaleId > 0)
                currencyColumn.ItemTemplate = new CurrencyColumn(field.Name, currencyLocaleId, formatting);
            else
                currencyColumn.ItemTemplate = new CurrencyColumn(field.Name, formatting);
            if (!string.IsNullOrEmpty(fieldRef.DisplayName))
                currencyColumn.HeaderText = fieldRef.DisplayName;
            else
                currencyColumn.HeaderText = field.DisplayName;
            currencyColumn.SortExpression = field.Name;
            Columns.Add(currencyColumn);
        }

        private void AddDefaultColumn(Field field, FieldReference fieldRef)
        {
            // Add a normal column.
            BoundField column1 = new BoundField();
            column1.HeaderStyle.HorizontalAlign = GetHorizontalAlign(field);
            column1.ItemStyle.HorizontalAlign = GetHorizontalAlign(field);
            column1.DataField = field.Name;
            if (!string.IsNullOrEmpty(fieldRef.DisplayName))
                column1.HeaderText = fieldRef.DisplayName;
            else
                column1.HeaderText = field.DisplayName;
            column1.SortExpression = field.Name;
            IDataFormatting formatting = field as IDataFormatting;
            if (formatting != null)
            {
                column1.DataFormatString = formatting.DataFormatString;
            }
            column1.HtmlEncode = false;
            Columns.Add(column1);
        }

        private void AddBooleanColumn(Field field, FieldReference fieldRef)
        {
            // Add a column that renders a readonly (non-clickable) checkbox.
            TemplateField boolColumn = new TemplateField();
            boolColumn.HeaderStyle.HorizontalAlign = GetHorizontalAlign(field);
            boolColumn.ItemStyle.HorizontalAlign = GetHorizontalAlign(field);
            boolColumn.ItemTemplate = new BooleanColumn(field.Name);
            if (!string.IsNullOrEmpty(fieldRef.DisplayName))
                boolColumn.HeaderText = fieldRef.DisplayName;
            else
                boolColumn.HeaderText = field.DisplayName;
            boolColumn.SortExpression = field.Name;
            Columns.Add(boolColumn);
        }

        private HorizontalAlign GetHorizontalAlign(Field field)
        {
            switch (field.FieldType)
            {
                case FieldType.Currency:
                case FieldType.Number:
                    return HorizontalAlign.Right;
                default:
                    if (SPContext.Current.RegionalSettings.IsRightToLeft)
                    {
                        return HorizontalAlign.Right;
                    }
                    else
                    {
                        return HorizontalAlign.Left;
                    }
            }
        }

        private void SharePointGrid_PageIndexChanging(object sender, GridViewPageEventArgs e)
        {
            PageIndex = e.NewPageIndex;
            DataBind();
        }

        /// <summary>
        /// Event handler for sorting the SharePoint grid.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _spGrid_Sorting(object sender, GridViewSortEventArgs e)
        {
            string lastExpression = "";
            if (ViewState["SortExpression"] != null)
                lastExpression = ViewState["SortExpression"].ToString();
            string lastDirection = "asc";
            if (ViewState["SortDirection"] != null)
                lastDirection = ViewState["SortDirection"].ToString();

            string newDirection = "asc";
            if (e.SortExpression == lastExpression)
                newDirection = (lastDirection == "asc") ? "desc" : "asc";

            ViewState["SortExpression"] = e.SortExpression;
            ViewState["SortDirection"] = newDirection;

            PageIndex = 0;
            _customSort = true;
        }

        private bool HasQuery
        {
            get
            {
                return (GridDatasource != null && Configuration.Filters.Count > 0);
            }
        }

        /// <summary>
        /// PreRender; databind the grid.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreRender(EventArgs e)
        {
            if (GridDatasource != null)
            {
                try
                {
                    if (_customSort)
                        SetGroupAndSort();
                   
                    SetFilters();

                    if (!Configuration.JustSearchOnQuery || HasQuery)
                    {

                        _view = new System.Data.DataView(GridDatasource.GetData());

                        _view.Sort = _sortExpression;
                        DataSource = _view;

                        // Setup paging
                        if (AllowPaging)
                            PagerTemplate = null;  // Must be called after Controls.Add(oGrid)

                        // Bind the grid to the datasource.
                        DataBind();
                    }
                }
                catch (Exception ex)
                {
                    // Message is rendered by the webpart. Prevents the SharePoint page from 'crashing'.
                    _exception = ex;
                }
            }
            base.OnPreRender(e);
        }

        private void SetFilters()
        {
            // Setup Filters
            if (FilterProviders.Count > 0)
            {
                foreach (IFilterValues filter in FilterProviders)
                {
                    if (filter != null && filter.ParameterValues != null &&
                        GridDatasource.Fields.GetFieldsWithDisplayName(filter.ParameterName) != null)
                    {
                        foreach (Field f in GridDatasource.Fields.GetFieldsWithDisplayName(filter.ParameterName))
                        {
                            TST.SharePoint.Shared.Filter newfilter = new Filter(f);
                            foreach (string val in filter.ParameterValues)
                            {
                                newfilter.FilterValues.Add(new FilterValue(val));
                            }
                            Configuration.Filters.Add(newfilter);
                        }
                    }
                }
            }
        }

        private void SetGroupAndSort()
        {
            if (GridDatasource == null)
                return;

            // If a group by fields is configured, set up grouping in the grid.
            if ((Field.IsFieldId(Configuration.GroupBy)) &&
                (GridDatasource.Fields.Contains(Configuration.GroupBy)))
            {
                AllowGrouping = true;
                AllowGroupCollapse = true;
                GroupField = GridDatasource.Fields[Configuration.GroupBy].Name;
                GroupFieldDisplayName = GridDatasource.Fields[Configuration.GroupBy].DisplayName;
            }
            else
            {
                AllowGrouping = false;
                AllowGroupCollapse = false;
                GroupField = string.Empty;
                GroupFieldDisplayName = string.Empty;
            }

            _sortExpression = string.Empty;
            string groupSort = string.Empty;
            if (Field.IsFieldId(Configuration.GroupBy) && GridDatasource.Fields.Contains(Configuration.GroupBy))
            {
                Field groupField = GridDatasource.Fields[Configuration.GroupBy];
                groupSort = string.Format("{0} ASC", groupField.Name);
            }
            if (_customSort)
            {
                if (ViewState["SortExpression"] != null)
                {
                    string expression = "";
                    expression = ViewState["SortExpression"].ToString();
                    string direction = "asc";
                    if (ViewState["SortDirection"] != null)
                        direction = ViewState["SortDirection"].ToString();
                    if (!string.IsNullOrEmpty(expression))
                    {
                        if (!string.IsNullOrEmpty(groupSort))
                            groupSort += ", ";
                        _sortExpression = string.Format("{2}{0} {1}", expression, direction, groupSort);
                    }
                }
            }
            else
            {
                // Set the sorting options, if a custom sort is not set.
                if (Field.IsFieldId(Configuration.SortBy) && GridDatasource.Fields.Contains(Configuration.SortBy))
                {
                    Field sortField = GridDatasource.Fields[Configuration.SortBy];
                    if (!string.IsNullOrEmpty(groupSort))
                        groupSort += ", ";
                    _sortExpression = string.Format("{2}{0} {1}", sortField.Name, Configuration.SortOrder, groupSort);
                }
                else
                {
                    _sortExpression = groupSort;
                }
            }

        }

        /// <summary>
        /// If there has been an exception, render the message; otherwise render the grid.
        /// </summary>
        /// <param name="writer"></param>
        protected override void Render(HtmlTextWriter writer)
        {
            if (_exception != null)
            {
                writer.WriteLine(string.Format("Error while loading the data: {0}<br>", _exception.Message));
                writer.WriteLine(string.Format("Exception: {0}<br>", _exception.GetType().FullName));
                writer.WriteLine(string.Format("Stack trace: {0}", _exception.StackTrace));
                if (_exception.InnerException != null)
                {
                    writer.WriteLine(string.Format("Inner exception: {0}<br>", _exception.InnerException.Message));
                    writer.WriteLine(string.Format("Inner exception: {0}<br>", _exception.InnerException.GetType().FullName));
                    writer.WriteLine(string.Format("Inner exception stack trace: {0}", _exception.InnerException.StackTrace));
                }
            }
            else
            {
                base.Render(writer);
            }
        }

        public List<IFilterValues> FilterProviders
        {
            get { return _filterProviders; }
            set { _filterProviders = value; }
        }

    }
}
